<script setup>
import { reactive, ref, onMounted, onUnmounted, nextTick, watch, computed } from "vue"; // 新增computed
import MainContent from '@/components/mainContent.vue'
import DetailPanel from '@/components/detailPanel.vue'
import path from "path-browserify";

// -------------------------- 1. 基础状态定义（不变） --------------------------
const ws = ref(null)
const isWsConnected = ref(false)
const reconnectTimer = ref(null)
// 新增：心跳超时计时器（检测后端是否按时发心跳）
const heartbeatTimeoutTimer = ref(null)
// 新增：心跳配置（与后端完全对齐）
const HEARTBEAT_CONFIG = {
  TIMEOUT: 55 * 1000, // 前端超时检测（比后端60秒短，避免误判）
  RESPONSE_MSG: JSON.stringify({ type: "HEARTBEAT_RESPONSE" }), // 心跳响应格式
  REQUEST_TYPE: "HEARTBEAT_REQUEST" // 后端心跳请求标识
};
const wsUrl = path.join(import.meta.env.BASE_URL, import.meta.env.DEV ? "wss://service.neucityai.com:30080/xjwxx/xjwxx-websocket" : "wss://http://192.168.1.19:8031/xjwxx/xjwxx-websocket")// 'wss://service.neucityai.com:30080/xjwxx/xjwxx-websocket';
const wsTopic = '/topic/school/student';

// 章节切换状态（无需缓存，刷新后重置为默认）
const activeTab = ref('第一部分');
const showText = ref(0); // 当前章节索引：0=第一部分，1=第二部分，2=第三部分
const selectedMark = ref(null);
const isShowDetail = ref(false);
const detailData = ref({ count: 0, numbers: [] });

// -------------------------- 2. 核心数据定义（不变） --------------------------
const defaultData = {
  tabsList: ['第一部分', '第二部分', '第三部分'],
  htmlContentList: [
    '<p>路过一片荆棘丛时，陶陶遇到了蜘蛛。得知她要去参加狮王的婚礼，蜘蛛哈哈大笑，巨大的蜘蛛网被笑声震得剧烈地颤动起来。</p><p>“婚礼两周后就要举行了，你可是慢得出奇呀，怎么可能赶得上呢?”陶陶看了看自己的腿--它们虽然短小，却很结实。她满怀信心地说:“我会准时赶到那里的。”</p><p>蜘蛛充满同情地劝她:“陶陶，我的腿不但比你的灵巧，还多一倍呢，连我都觉得路途太遥远，你还是清醒点儿，赶紧回家吧!”“很遗憾，我不能这样，我的决定是不可改变的。”陶陶友好地回答，又哼哧哼哧地开始赶路了。 </p>',
    '<p>越过种种障碍，穿过树林和沙地，陶陶日夜不停地赶路。</p><p>有一天，陶陶经过一个池塘，她想停下来喝口水。在一片常春藤的叶子上，一只蜗牛正瞪着双眼打量着她。陶陶客气地跟蜗牛打听去狮王二十八世的宫殿怎么走。</p><p>蜗牛想了好一会儿，才把自己那迷迷糊糊的思绪理清楚。他难过地告诉陶陶:“太糟了!你完全走反了方向。</p><p> “非常感谢你给我指路!”说完，陶陶慢慢掉头往回走。</p><p>“可是，后天就要举行婚礼了呀!”蜗牛几乎带着哭腔说。</p><p>只要一步一步坚持走，我会准时赶到的。”陶陶说。</p>',
    '<p>越过种种障碍，穿过树林和沙地，陶陶日夜不停地赶路。</p><p>后来，她遇到了正躺在石头上打盹儿的壁虎。得知陶陶的来意，壁虎居高临下地打量着她说:“作为狮王王宫的高级官员，我正式通知你，婚礼暂时取消了。由于非常突然的原因，狮王二十八世不得不和老虎开战。你可以回家了。”</p><p>......</p>'
  ],
  pureTextList: []
};

// 从localStorage读取缓存数据（封装工具函数）
const getCache = (key) => {
  try {
    const cache = localStorage.getItem(key);
    return cache ? JSON.parse(cache) : null;
  } catch (err) {
    console.error(`读取缓存${key}失败：`, err);
    localStorage.removeItem(key);
    return null;
  }
};

// 初始化核心数据（优先用缓存）
const dataVal = reactive({
  ...getCache('studentTextData') || defaultData,
  pureTextList: []
});
const allMarks = reactive(getCache('studentMarksData') || []);

// -------------------------- 3. 新增：计算属性（实时过滤当前章节的标注） --------------------------
// 关键：根据当前章节索引（showText）过滤标注，仅保留当前章节的标注
const currentChapterMarks = computed(() => {
  return allMarks.filter(mark => {
    // 关联规则：mark.paragraph（1/2/3） = showText（0/1/2） + 1
    return mark.paragraph === showText.value + 1;
  });
});
// 2. 核心：根据选中状态，返回“单个标注”或“全部标注”
const filteredMarks = computed(() => {
  // 若有选中的标注，且该标注属于当前章节 → 仅返回这一个标注
  if (selectedMark.value && currentChapterMarks.value.some(mark => mark.content === selectedMark.value.content)) {
    return [selectedMark.value];
  }
  // 若无选中标注 → 返回当前章节的全部标注
  return currentChapterMarks.value;
});
// -------------------------- 4. 缓存更新逻辑（不变） --------------------------
watch(allMarks, (newMarks) => {
  localStorage.setItem('studentMarksData', JSON.stringify(newMarks));
}, { deep: true });

watch(dataVal, (newData) => {
  const cacheData = {
    tabsList: newData.tabsList,
    htmlContentList: newData.htmlContentList
  };
  localStorage.setItem('studentTextData', JSON.stringify(cacheData));
}, { deep: true });

const initPureText = () => {
  dataVal.pureTextList = dataVal.htmlContentList.map(html => {
    const tempDiv = document.createElement('div');
    tempDiv.innerHTML = html;
    return tempDiv.textContent.trim();
  });
};

const changeTab = (tab, idx) => {
  activeTab.value = tab;
  showText.value = idx;
  selectedMark.value = null; // 新增：切换章节时清空选中的标注，避免状态残留
  nextTick(() => {
    renderUnderlines();
  });
};
const handleMarkClick = (mark) => {
  selectedMark.value = mark; // 记录当前选中的标注
  nextTick(() => {
    renderUnderlines(); // 关键：点击后立即重新渲染，只显示该标注
  });
};
const handleMarkDblClick = () => {
  selectedMark.value = null; // 重置选中状态（取消当前选中的标注）
  nextTick(() => {
    renderUnderlines(); // 重新渲染：恢复显示当前章节的全部标注下划线
  });
};
const handleDetail = (item) => {
  detailData.value.count = item.students.length;
  detailData.value.numbers = item.students;
  isShowDetail.value = true;
};

const closeDetailPanel = () => {
  isShowDetail.value = false;
};

const renderUnderlines = () => {
  const textContainer = document.querySelector(`.text-container-${showText.value}`);
  if (!textContainer) return;

  const currentPureText = dataVal.pureTextList[showText.value];
  if (!currentPureText) return;

  // 1. 重置文本（清除旧下划线）
  textContainer.innerHTML = dataVal.htmlContentList[showText.value];

  // 2. 核心过滤逻辑：选中时只渲染单个标注，未选中时渲染全部
  const marksToRender = selectedMark.value
    ? currentChapterMarks.value.filter(mark => mark.content === selectedMark.value.content)
    : currentChapterMarks.value;

  // 3. 以下“生成下划线”的逻辑保持不变...
  marksToRender.forEach(mark => {
    if (!currentPureText.includes(mark.content)) return;

    const treeWalker = document.createTreeWalker(
      textContainer,
      NodeFilter.SHOW_TEXT,
      (node) => node.textContent.includes(mark.content) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
    );

    const textNodes = [];
    let currentNode;
    while ((currentNode = treeWalker.nextNode())) {
      textNodes.push(currentNode);
    }

    textNodes.forEach(textNode => {
      let text = textNode.textContent;
      let index = text.indexOf(mark.content);

      while (index !== -1) {
        const beforeText = text.slice(0, index);
        const matchText = text.slice(index, index + mark.content.length);
        const afterText = text.slice(index + mark.content.length);

        const markElement = document.createElement('span');
        markElement.className = `text-mark`;
        markElement.style.textDecoration = 'underline';
        markElement.style.textDecorationColor = '#E95294';
        markElement.style.textDecorationThickness = `${1 + Math.min(2, mark.students.length / 2)}px`;
        markElement.style.backgroundColor = 'rgba(233, 82, 148, 0.05)';
        markElement.textContent = matchText;

        textNode.textContent = beforeText;
        textNode.parentNode.insertBefore(markElement, textNode.nextSibling);

        if (afterText) {
          const afterNode = document.createTextNode(afterText);
          textNode.parentNode.insertBefore(afterNode, markElement.nextSibling);
          text = afterText;
          index = text.indexOf(mark.content);
        } else {
          index = -1;
        }
      }
    });
  });
};

const handleWebSocketMark = (markContentList, paragraph, student) => {
  const contents = Array.isArray(markContentList) ? markContentList : [markContentList];

  contents.forEach(content => {
    const existingMark = allMarks.find(mark =>
      mark.content === content && mark.paragraph === paragraph
    );

    if (existingMark) {
      if (!existingMark.students.includes(student)) {
        existingMark.students.push(student);
      }
    } else {
      allMarks.push({
        content: content,
        paragraph: paragraph,
        students: [student]
      });
    }
  });

  nextTick(() => {
    renderUnderlines();
  });
};
// 收到后端任何消息时调用，重置超时计时器
const resetHeartbeatTimeout = () => {
  if (heartbeatTimeoutTimer.value) {
    clearTimeout(heartbeatTimeoutTimer.value);
  }
  // 超时后判定连接异常，触发重连
  heartbeatTimeoutTimer.value = setTimeout(() => {
    console.error('WebSocket心跳超时，触发重连');
    if (ws.value) {
      ws.value.close(999, '心跳超时');
    }
    triggerReconnect();
  }, HEARTBEAT_CONFIG.TIMEOUT);
};
const initWebSocket = () => {
  if (ws.value && isWsConnected.value) return;

  try {
    ws.value = new WebSocket(wsUrl);

    ws.value.onopen = () => {
      isWsConnected.value = true;
      console.log('WebSocket连接成功，开始监听标注消息');
      // 新增：连接成功后启动心跳超时检测
      resetHeartbeatTimeout();
      sendSubscribeMessage();
    };

    ws.value.onmessage = (event) => {
      try {
        const messageData = JSON.parse(event.data);
        // 新增：收到任何消息，先重置心跳超时计时器
        resetHeartbeatTimeout();
        const outerData = messageData.data;

        if (outerData?.type === 'commit' && outerData?.data?.type === 'mark') {
          const { markContent, paragraph, student } = outerData.data.content || {};
          if (markContent && paragraph && student) {
            handleWebSocketMark(markContent, paragraph, student);
          }
        }
      } catch (err) {
        console.error('WebSocket消息解析失败：', err);
      }
    };

    ws.value.onerror = (error) => {
      isWsConnected.value = false;
      console.error('WebSocket错误：', error);
      if (heartbeatTimeoutTimer.value) clearTimeout(heartbeatTimeoutTimer.value);
      triggerReconnect();
    };

    ws.value.onclose = (event) => {
      isWsConnected.value = false;
      if (event.code !== 1000) {
        if (heartbeatTimeoutTimer.value) clearTimeout(heartbeatTimeoutTimer.value);
        console.error(`WebSocket关闭（状态码：${event.code}），正在重连...`);
        triggerReconnect();
      }
    };
  } catch (err) {
    console.error('WebSocket初始化失败：', err);
  }
};

const sendSubscribeMessage = () => {
  if (!ws.value || !isWsConnected.value) return;

  const subscribeMsg = {
    command: 'SUBSCRIBE',
    destination: wsTopic
  };

  try {
    ws.value.send(JSON.stringify(subscribeMsg));
    console.log('已发送订阅请求：', subscribeMsg);
  } catch (err) {
    console.error('订阅消息发送失败：', err);
  }
};

const triggerReconnect = () => {
  if (reconnectTimer.value) clearTimeout(reconnectTimer.value);
  reconnectTimer.value = setTimeout(() => {
    console.log('尝试重新连接WebSocket...');
    initWebSocket();
  }, 3000);
};

// -------------------------- 6. 生命周期钩子（不变） --------------------------
onMounted(() => {
  initPureText();
  initWebSocket();
  nextTick(() => {
    renderUnderlines();
  });
});

onUnmounted(() => {
  if (ws.value) {
    ws.value.close(1000, '页面关闭，主动断开连接');
    ws.value = null;
  }
  // 新增：页面卸载时清除心跳超时计时器
  if (heartbeatTimeoutTimer.value) {
    clearTimeout(heartbeatTimeoutTimer.value);
  }
  if (reconnectTimer.value) {
    clearTimeout(reconnectTimer.value);
  }
});
</script>

<template>
  <MainContent contentWidth="100%" padding="0">
    <template #content>
      <div class="container d-flex gap-1 flex-1 height100">
        <!-- 左侧：文本显示区（不变） -->
        <div class="content width70 borderbox">
          <div class="d-flex space-between text-tabs">
            <div class="tab-item" :class="{ active: activeTab === tab }" @click="changeTab(tab, idx)"
              v-for="(tab, idx) in dataVal.tabsList" :key="idx">
              {{ tab }}
            </div>
          </div>
          <div class="selectable-text">
            <div class="text-container" :class="`text-container-${idx}`" v-for="(_, idx) in dataVal.htmlContentList"
              :key="idx" v-show="showText === idx"></div>
          </div>
        </div>

        <!-- 右侧：学生标注列表区（核心修改：用currentChapterMarks替代allMarks） -->
        <div class="result-panel width30 borderbox d-flex direction-column align-items-center">
          <template v-if="!isShowDetail">
            <h3 class="panel-title">———&nbsp;&nbsp;&nbsp;&nbsp;学生标注&nbsp;&nbsp;&nbsp;&nbsp;———</h3>

            <!-- 无标注提示：基于当前章节的标注数量判断 -->
            <div v-if="currentChapterMarks.length === 0" class="no-mark-tip">
              暂无当前章节标注数据
            </div>

            <!-- 标注列表：仅渲染当前章节的标注 -->
            <div class="mark-item" v-for="(mark, idx) in currentChapterMarks" :key="idx"
              :class="{ active: selectedMark === mark }" @click="handleMarkClick(mark)" @dblclick="handleMarkDblClick">
              <span class="mark-count">{{ mark.students.length }}人</span>
              <span class="mark-number" v-for="(student, sIdx) in mark.students" :key="sIdx">{{ student }}</span>
              <button class="detail-btn" @click.stop="handleDetail(mark)">详情</button>
            </div>
          </template>

          <!-- 详情面板（不变） -->
          <template v-else>
            <DetailPanel :count="detailData.count" :numbers="detailData.numbers" @close="closeDetailPanel" />
          </template>
        </div>
      </div>
    </template>
  </MainContent>
</template>

<style scoped lang="scss">
.container {
  max-width: 90%;
  overflow-y: auto;
  margin: 0 auto;
  padding: 20px;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* 章节标签栏样式 */
.content {
  border-radius: 12px;
  padding: 10px 20px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);

  .text-tabs {
    margin: 0 auto 20px;
    box-sizing: border-box;
    width: 540px;
    height: 60px;
    border-radius: 50px;
    background: #E9529426;
    overflow: hidden;

    .tab-item {
      font-family: Alibaba PuHuiTi 2.0;
      font-weight: 400;
      font-size: 24px;
      line-height: 60px;
      text-align: center;
      flex: 1;
      cursor: pointer;

      &.active {
        color: #fff;
        background-color: #E95294;
      }
    }
  }

  .selectable-text {
    height: calc(100% - 100px);
    overflow-y: auto;
    -webkit-user-select: text;
    user-select: text;
    font-family: KaiTi;
    font-weight: 400;
    font-size: 1.625rem;
    line-height: 2.625rem;
    text-align: justify;
    text-indent: 2em;
    color: #333;
  }

  .text-container {
    width: 100%;
  }
}

/* 右侧标注列表样式 */
.result-panel {
  height: 100%;
  border-radius: 12px;
  padding: 20px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
  overflow-y: auto;

  .panel-title {
    color: #e95294;
    font-size: 20px;
    margin-bottom: 20px;
    text-align: center;
    width: 100%;
  }

  .no-mark-tip {
    color: #999;
    font-size: 16px;
    margin-top: 20px;
  }

  .mark-item {
    background: #f4efef;
    border-radius: 4px;
    display: flex;
    align-items: center;
    padding: 10px;
    border-bottom: 1px solid #eee;
    width: 100%;
    height: auto;
    /* 自适应高度，避免学生号过多溢出 */
    margin-top: 8px;
    cursor: pointer;
    flex-wrap: wrap;
    /* 学生号过多时换行 */

    &:last-child {
      border-bottom: none;
    }

    &.active {
      border: 2px solid #e95294;
      border-radius: 6px;
    }

    .mark-count {
      min-width: 60px;
      margin-right: 15px;
      font-size: 18px;
      color: #333;
    }

    .mark-number {
      display: inline-block;
      width: 30px;
      height: 30px;
      font-size: 16px;
      line-height: 30px;
      text-align: center;
      background-color: #fff;
      border: 1px solid #ddd;
      border-radius: 50%;
      margin: 0 5px 5px 0;
      /* 横向+纵向间距，避免拥挤 */
    }

    .detail-btn {
      margin-left: auto;
      background-color: #e95294;
      color: #fff;
      border: none;
      padding: 5px 12px;
      border-radius: 4px;
      cursor: pointer;
      margin-top: 5px;
      /* 换行后与学生号保持间距 */
    }
  }
}

/* 下划线标记样式（可通过JS动态调整） */
.text-mark {
  transition: all 0.2s ease;
}

.text-mark:hover {
  background-color: rgba(233, 82, 148, 0.1);
}
</style>
